package com.zhao.dota.security.store;

import com.zhao.dota.bo.AuthenticateUser;
import com.zhao.dota.security.AuthenticationKeyGenerator;
import com.zhao.dota.security.token.AccessToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

@Slf4j
public class EhcacheTokenStore implements TokenStore {

    private static final String ACCESS_TOKEN_KEY_PREFIX = "token:access:";
    private static final String AUTHENTICATE_USER_KEY_PREFIX = "token:authenticateUser:";
    private static final String ACCESS_TOKEN_TO_REFRESH_TOKEN_KEY_PREFIX = "token:accessTokenToRefreshToken:";
    private static final String REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX = "token:refreshTokenToAccessToken:";
    private static final String REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX = "token:refreshTokenAuthentication:";
    private static final String AUTHENTICATION_TO_ACCESS_TOKEN_KEY_PREFIX = "token:authenticationToAccessToken:";
    private final AuthenticationKeyGenerator keyGenerator = new AuthenticationKeyGenerator();

    private final Cache cache;

    public EhcacheTokenStore(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("token");
    }

    @Override
    public AccessToken readAccessToken(String token) {
        return cache.get(ACCESS_TOKEN_KEY_PREFIX + token, AccessToken.class);
    }

    @Override
    public AuthenticateUser readAuthentication(AccessToken accessToken) {
        if (accessToken != null) {
            return cache.get(AUTHENTICATE_USER_KEY_PREFIX + accessToken.getValue(), AuthenticateUser.class);
        }
        return null;
    }

    @Override
    public AuthenticateUser readAuthentication(String token) {
        return cache.get(AUTHENTICATE_USER_KEY_PREFIX + token, AuthenticateUser.class);
    }

    @Override
    public void storeAccessToken(AccessToken token, AuthenticateUser authentication) {
        String accessToken = token.getValue();
        String refreshToken = token.getRefreshToken();

        cache.put(ACCESS_TOKEN_KEY_PREFIX + accessToken, token);
        cache.put(AUTHENTICATE_USER_KEY_PREFIX + accessToken, authentication);
        cache.put(ACCESS_TOKEN_TO_REFRESH_TOKEN_KEY_PREFIX + accessToken, refreshToken);
        cache.put(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken, accessToken);
        cache.put(REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX + refreshToken, authentication);
        String key = keyGenerator.extractKey(authentication);
        cache.put(AUTHENTICATION_TO_ACCESS_TOKEN_KEY_PREFIX + key, token);
    }

    @Override
    public void removeAccessToken(AccessToken token) {
        if (token == null) {
            return;
        }
        String accessToken = token.getValue();
        String refreshToken = token.getRefreshToken();
        AuthenticateUser authentication = readAuthentication(accessToken);
        String key = keyGenerator.extractKey(authentication);

        cache.evict(ACCESS_TOKEN_KEY_PREFIX + accessToken);
        cache.evict(AUTHENTICATE_USER_KEY_PREFIX + accessToken);
        cache.evict(ACCESS_TOKEN_TO_REFRESH_TOKEN_KEY_PREFIX + accessToken);
        cache.evict(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken);
        cache.evict(REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX + refreshToken);
        cache.evict(AUTHENTICATION_TO_ACCESS_TOKEN_KEY_PREFIX + key);
    }

    @Override
    public void removeAccessToken(String token) {
        AccessToken accessToken = readAccessToken(token);
        removeAccessToken(accessToken);
    }

    @Override
    public void storeRefreshToken(String refreshToken, AuthenticateUser authentication) {
        cache.put(REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX + refreshToken, authentication);
    }

    @Override
    public String readRefreshToken(String accessToken) {
        return cache.get(ACCESS_TOKEN_TO_REFRESH_TOKEN_KEY_PREFIX + accessToken, String.class);
    }

    @Override
    public AuthenticateUser readAuthenticationForRefreshToken(String refreshToken) {
        return cache.get(REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX + refreshToken, AuthenticateUser.class);
    }

    @Override
    public void removeRefreshToken(String refreshToken) {
        cache.evict(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken);
        cache.evict(REFRESH_TOKEN_AUTHENTICATION_KEY_PREFIX + refreshToken);
    }

    @Override
    public void removeAccessTokenUsingRefreshToken(String refreshToken) {
        cache.evict(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken);
        AccessToken accessToken = cache.get(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken, AccessToken.class);;
        if (accessToken != null) {
            removeAccessToken(accessToken);
        }
    }

    @Override
    public AccessToken getAccessToken(AuthenticateUser authentication) {
        String key = keyGenerator.extractKey(authentication);
        return cache.get(AUTHENTICATION_TO_ACCESS_TOKEN_KEY_PREFIX + key, AccessToken.class);
    }

    @Override
    public AccessToken getAccessToken(String refreshToken) {
        String accessToken = cache.get(REFRESH_TOKEN_TO_ACCESS_TOKEN_KEY_PREFIX + refreshToken, String.class);
        return readAccessToken(accessToken);
    }

}
